# Copyright (C) 2010 Iain Surgey.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
#
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import sys
import rx

class Inline:
	
	def __init__(self,convert):
		self.cv = convert
	
	def parse(self,line):
		out = ""
		loc = line.find("=")
		
		if line[:2] == "m|":
			if loc > -1:
				line = line[2:]
				loc = loc - 2
				lt = line[:loc].strip()
				rt = line[loc+1:].strip()
				nv,lt,isemb = self.newval(lt,True)
				lt = lt.strip()
				nv = nv.strip()
				if len(lt) > 0 or len(nv) < 1:
					self.SyntaxError("Invalid assignment | "+nv+"  "+lt)
				if len(nv) > 14 and nv[:10] == "Memory.set":
					out = nv+"__m__.length); \n"
				else:
					out = nv+" = __m__.length; \n"
				return out+"__m__.length += "+self.translate(rt)
			else:
				out = "__m__.length; \n"
				line = line[2:]
				return out+"__m__.length += "+self.translate(line)
		elif loc > -1:
			lt = line[:loc]
			rt = line[loc+1:]
			nv,lt,isemb = self.newval(lt,True)
			lt = lt.strip()
			nv = nv.strip()
			if len(lt) > 0 or len(nv) < 1:
				self.SyntaxError("Invalid assignment | "+nv+" | "+lt)
			if len(nv) > 14 and nv[:10] == "Memory.set":
				out = nv+self.translate(rt)+")"
			else:
				out = nv+" = "+self.translate(rt)
			return out
		else:
			return self.translate(line)
	
	def translate(self,line):
		out = ""
		while True:
			nv,line = self.rmbrack(line)
			out = out+nv
			
			nv,line = self.rmneg(line)
			out = out+nv
			
			nv,line = self.rmbrack(line)
			out = out+nv
			
			nv,line,isemb = self.newval(line,False)
			out = out+nv
			
			nv,line = self.rmbrack(line)
			out = out+nv
			
			line = line.strip()
			if len(line) < 1:
				break
			
			m = rx.operator.match(line)
			if m:
				line = line[m.end():]
				out = out+m.group(1)
			#else:
			#	if not isemb:
			#		self.SyntaxError("Expecting operator - "+line)
		
		return out
	
	def evalarr(self,line,v,out,ofs,isofs=True):
		count = 0
		
		maxp = v.ptr
		if len(v.arr) > 0:
			maxp = maxp+1
		maxa = len(v.arr)+v.ptr
		
		while True:
		
			line = line.lstrip()
			if len(line) < 1 or line[:1] != "[":
				break
			line = line[1:].lstrip()
			
			count = count+1
			mult = ""
			
			if count > len(v.arr)-1:
				maxp = maxp - 1
			
			if count > len(v.arr):
				
				if count-len(v.arr) > v.ptr:
					self.SyntaxError("Too many array dimensions, max = "+str(maxa)+" | "+v.name)
				if isofs == False:
					out = self.ofsapp(out,ofs)
					isofs = True
				else:
					out = "Memory.getI32("+self.ofsapp(out,ofs)+")"
				ofs = 0
				
				if maxp > 0:
					mult = "<<2"
				else:
					if v.type.len == 1:
						mult = ""
					else:
						mult = str(v.type.len)
						if mult in self.cv.bitsh.keys():
							mult = self.cv.bitsh[mult]
						else:
							mult = " * "+mult
			else:
				mult = str(v.arr[count-1])
			
			tout = "("
			
			while True:
				
				nv,line = self.rmbrack(line)
				tout = tout+nv
				
				nv,line = self.rmneg(line)
				tout = tout+nv
				
				nv,line = self.rmbrack(line)
				tout = tout+nv
				
				nv,line,isemb = self.newval(line,False)
				tout = tout+"("+nv+")"
				
				nv,line = self.rmbrack(line)
				tout = tout+nv
				
				line = line.strip()
				if len(line) < 1:
					self.SyntaxError("Unexpected end of array statement")
				if line[:1] == "]":
					line = line[1:]
					break
				
				m = rx.operator.match(line)
				if m:
					line = line[m.end():]
					tout = tout+m.group(1)
				#else:
				#	if not isemb:
				#		self.SyntaxError("Expecting operator in array - "+line)
					
			tout = tout+")"+mult
			#print mult
			#print tout
			
			tval = 0
			m = rx.alpha.search(tout)
			if m and len(m.group().strip()) > 0:
				if count > 0 and len(out.strip()) > 0:
					out = out+"+"
				out = out+"("+tout+")"
			else:
				try:
					tval = eval(tout)
				except:
					self.SyntaxError("Error evaluating numerical array | "+tout)
				#out = out+"("+str(tval)+")"
				ofs = ofs+tval
		
		#if count < len(v.arr):
		#	maxp = maxp-1
		
		return out,ofs,line,maxp,isofs
	
	def newval(self,line,set=False):
		
		isemb = False
		
		line = line.strip()
		if len(line) < 1:
			self.SyntaxError("Expecting something")
		
		out = ""
		m = rx.float.match(line)
		if m:
			out = line[:m.end()]
			line = line[m.end():]
		else:
			m = rx.embed.match(line)
			if m:
				out = m.group(1)
				line = line[m.end():]
				isemb = True
			else:
				m = rx.sizeof.match(line)
				if m:
					ty = m.group(1)
					if ty not in self.cv.types.keys():
						self.SyntaxError("sizeof type not found | "+m.group(1))
					t = self.cv.types[ty]
					out = "("+str(t.len)+")"
					line = line[m.end():]
				else:
					m = rx.inlvar.match(line)
					if m:
						out,line = self.transval(m,line[m.end():],set)
					else:
						self.SyntaxError("aWTF is this? - "+line)
		return out,line,isemb
	
	def transval(self,m,line,set):
		varnm = m.group(2)
		arrli = []
		out = ""
		ofs = 0
		dref = self.countderef(m.group(1))
		
		
		if varnm not in self.cv.vars.keys():
			self.SyntaxError("Unknown identifier | "+varnm)
		v = self.cv.vars[varnm]
		
		isofs = True
		
		if len(v.arr) < 1:
			if v.ptr > 0:
				isofs = False
			if v.ptr < 1 and v.type.basic == True:
				isofs = False
		
		if isofs == False:
			out = self.nvapp(out,varnm)
		
		out,ofs,line,maxp,isofs = self.evalarr(line,v,out,ofs+v.ofs,isofs)
		
		line = line.strip()
		if v.type.basic == False and maxp > 0 and line[:2] == "->":
			if maxp > 1:
				self.SyntaxError("Cannot access through dereferenced multi-pointer | "+m.group())
			return self.transtruct(line[2:],0,self.ofsapp(out,ofs+v.ofs),v,dref,set)
		if v.type.basic == False and maxp == 0 and line[:1] == ".":
			return self.transtruct(line[1:],ofs,out,v,dref,set)
		
		if dref < 0:
			if isofs == False:
				self.SyntaxError("Cannot reference this type at this level | "+m.group())
			return self.ofsapp(out,ofs),line
		
		if isofs:
			out = self.ofsapp(out,ofs)
		out = self.derefer(out,dref-1,maxp,set,isofs)
		
		if dref < maxp:
			if isofs:
				if set:
					return "Memory.setI32("+out+",",line
				if v.ptr >= maxp:
					return "Memory.getI32("+out+")",line
			return out,line
		
		if isofs:
			if v.type.basic:
				return self.getsetbasic(v,out,set),line
			else:
				self.SyntaxError("Cannot copy struct, do it yourself | "+m.group())
		elif maxp > 0:
			return self.getsetbasic(v,out,set),line
		return out,line
	
	
	def transtruct(self,line,ofs,out,lv,dref,set):
		
		m = rx.stvar.match(line)
		if not m:
			self.SyntaxError("Expecting struct variable | "+line)
		
		line = line[m.end():]
		varnm = m.group(1)
		arrli = []
		
		if varnm not in lv.type.vars.keys():
			self.SyntaxError("Unknown identifier | "+varnm)
		v = lv.type.vars[varnm]
		
		out,ofs,line,maxp,isofs = self.evalarr(line,v,out,ofs+v.ofs)
		
		line = line.strip()
		
		if v.type.basic == False and maxp > 0 and line[:2] == "->":
			if maxp > 1:
				self.SyntaxError("Cannot access multi-pointer through dereference | "+m.group())
			return self.transtruct(line[2:],0,"Memory.getI32("+self.ofsapp(out,ofs)+")",v,dref,set)
		if v.type.basic == False and maxp == 0 and line[:1] == ".":
			return self.transtruct(line[1:],ofs,out,v,dref,set)
		
		if dref < 0:
			return self.ofsapp(out,ofs),line
		else:
			out = self.ofsapp(out,ofs)
			out = self.derefer(out,dref,maxp,set)
			
			if dref < maxp:
				if v.ptr >= maxp:
					if set == True:
						out = "Memory.setI32("+out+","
					else:
						out = "Memory.getI32("+out+")"
				return out,line
			
			if v.type.basic:
				return self.getsetbasic(v,out,set),line
			else:
				self.SyntaxError("Cannot copy struct, do it yourself | "+m.group())
		
		self.SyntaxError("Shouldn't get here | "+m.group())
		
	
	def derefer(self,val,dref,max,set,isofs=True):
		if dref > max:
			self.SyntaxError("Too many dereferences")
		
		out = val
		
		#print "max : "+str(max)
		
		for i in range(0,dref):
			#if i+1 >= dref:
			#	break
			if i == 0 and isofs == False:
				continue
			out = "Memory.getI32("+out+")"
			
		return out
	
	
	def strofs(self,ofs):
		if ofs != 0:
			return str(ofs)
		return ""
	
	def ofsapp(self,val,ofs):
		ostr = ""
		if ofs != 0:
			ostr = str(ofs)
		if len(ostr.strip()) > 0 and len(val.strip()) > 0:
			return val+" + "+str(ofs)
		return val+" "+ostr
	
	def nvapp(self,val,nv):
		if len(nv.strip()) > 0 and len(val.strip()) > 0:
			return val+"+ "+nv
		return val+" "+nv
	
	def getsetbasic(self,v,val,set):
		
		if v.type.name == "int" or v.type.name == "long":
			if set:
				return "Memory.setI32("+val+","
			else:
				return "Memory.getI32("+val+")"
		elif v.type.name == "short":
			if set:
				return "Memory.setI16("+val+","
			else:
				return "Memory.signExtend16(Memory.getUI16("+val+"))"
		elif v.type.name == "ushort":
			if set:
				return "Memory.setI16("+val+","
			else:
				return "Memory.getUI16("+val+")"
		elif v.type.name == "byte":
			if set:
				return "Memory.setByte("+val+","
			else:
				return "Memory.getByte("+val+")"
		elif v.type.name == "float":
			if set:
				return "Memory.setFloat("+val+","
			else:
				return "Memory.getFloat("+val+")"
		elif v.type.name == "double":
			if set:
				return "Memory.setDouble("+val+","
			else:
				return "Memory.getDouble("+val+")"
		else:
			self.SyntaxError("Basic type, "+v.type.name+" not supported")
	
	def countderef(self,drefst):
		
		dref = 0
		
		if drefst != None and len(drefst.strip()) > 0:
			drefst = drefst.strip()
			if drefst[:1] == "*":
				dref = 1
				drefst = drefst[1:].strip()
				while len(drefst) > 0:
					if drefst[:1] == "*":
						dref = dref+1
					else:
						self.SyntaxError("Invalid dereference value | "+drefst)
					drefst = drefst[1:].strip()
			elif drefst[:1] == "&":
				dref = -1
				drefst = drefst[1:].strip()
				if len(drefst) > 0:
					self.SyntaxError("Can only have one reference | "+drefst)
			else:
				self.SyntaxError("Invalid (de)reference value | "+drefst)
		
		return dref
	
	def rmbrack(self,line):
		out = ""
		while True:
			line = line.lstrip()
			if line[:1] == "(" or line[:1] == ")":
				out = out+line[:1]
				line = line[1:]
			else:
				break
		return out,line
	
	def rmneg(self,line):
		out = ""
		while True:
			line = line.lstrip()
			if line[:1] == "-":
				out = out+line[:1]
				line = line[1:]
			else:
				break
		return out,line
				
	def SyntaxError(self,msg):
		print "Syntax Error |: ["+self.cv.cf.name+" | line "+str(self.cv.cf.line)+"] "+msg
		sys.exit()
